package ace.module.sms.core.impl.service.impl;

import ace.cmp.core.model.R;
import ace.cmp.json.api.JsonService;
import ace.module.sms.api.model.input.SendTemplateInput;
import ace.module.sms.core.impl.model.request.SmsBatchSendItemRequest;
import ace.module.sms.core.impl.model.request.SmsBatchSendRequest;
import ace.module.sms.core.impl.model.response.SmsBatchSendResponse;
import ace.module.sms.core.impl.dao.entity.SmsApplication;
import ace.module.sms.core.impl.dao.entity.SmsApplicationConfig;
import ace.module.sms.core.impl.dao.entity.SmsSendRecord;
import ace.module.sms.core.impl.dao.entity.SmsSupplier;
import ace.module.sms.core.impl.manager.SmsApplicationConfigManager;
import ace.module.sms.core.impl.manager.SmsApplicationManager;
import ace.module.sms.core.impl.manager.SmsSendRecordManager;
import ace.module.sms.core.impl.manager.SmsSupplierManager;
import ace.module.sms.core.impl.manager.supplier.SmsSupplierProviderMgr;
import ace.module.sms.core.impl.service.SmsService;
import ace.module.sms.supplier.api.SmsHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.springframework.stereotype.Component;

/**
 * @author Caspar
 * @create 2020/11/5
 */
@Slf4j
@AllArgsConstructor
@Component
public class SmsServiceImpl implements SmsService {

  private final SmsApplicationManager smsApplicationManager;
  private final SmsApplicationConfigManager smsApplicationConfigManager;
  private final SmsSupplierManager smsSupplierManager;
  private final SmsSupplierProviderMgr smsSupplierProviderMgr;
  private final SmsSendRecordManager smsSendRecordManager;
  private final JsonService jsonService;

  @Override
  public R<Void> send(Long smsAppId, String phoneNumber, String content) {
    SmsApplication smsApplication = this.smsApplicationManager.getAndCheckAllById(smsAppId);

    SmsApplicationConfig smsApplicationConfig =
        this.smsApplicationConfigManager.getAndCheckAllById(smsAppId);

    SmsSupplier smsSupplierConfig =
        this.smsSupplierManager.getAndCheckAllById(smsApplicationConfig.getSmsSupplierId());

    SmsHandler smsHandler = smsSupplierProviderMgr.getAndCheckProvider(smsSupplierConfig);

    return this.send(
        smsApplication, smsApplicationConfig, smsSupplierConfig, smsHandler, phoneNumber, content);
  }

  @Override
  public R<Void> sendTemplate(SendTemplateInput sendTemplateInput) {
    SmsApplication smsApplication =
        this.smsApplicationManager.getAndCheckAllById(sendTemplateInput.getSmsAppId());

    SmsApplicationConfig smsApplicationConfig =
        this.smsApplicationConfigManager.getAndCheckAllById(sendTemplateInput.getSmsAppId());

    SmsSupplier smsSupplierConfig =
        this.smsSupplierManager.getAndCheckAllById(smsApplicationConfig.getSmsSupplierId());

    SmsHandler smsHandler = smsSupplierProviderMgr.getAndCheckProvider(smsSupplierConfig);

    return this.sendTemplate(
        smsApplication, smsApplicationConfig, smsSupplierConfig, smsHandler, sendTemplateInput);
  }

  @Override
  public R<Void> sendVerifyCode(Long smsAppId, String phoneNumber, String code) {
    SendTemplateInput sendTemplateInput =
        SendTemplateInput.builder()
            .smsAppId(smsAppId)
            .phoneNumber(phoneNumber)
            .contentParams(Map.of("code", code))
            .build();
    return this.sendTemplate(sendTemplateInput);
  }

  @Override
  public SmsBatchSendResponse send(SmsBatchSendRequest request) {
    SmsBatchSendResponse.SmsBatchSendResponseBuilder builder = SmsBatchSendResponse.builder();

    long successCount = 0L;
    long failureCount = 0L;
    List<String> successList = new ArrayList<>();
    List<String> failureList = new ArrayList<>();
    for (SmsBatchSendItemRequest smsBatchSendItemRequest : request.getSmsList()) {
      R<Void> result;
      try {
        result =
            this.send(
                request.getSmsAppId(),
                smsBatchSendItemRequest.getPhoneNumber(),
                smsBatchSendItemRequest.getContent());
      } catch (Exception ex) {
        result = R.fail(ex.getMessage());
      }
      if (result.success()) {
        successList.add(smsBatchSendItemRequest.getPhoneNumber());
        successCount = successCount + 1;
      } else {
        failureList.add(smsBatchSendItemRequest.getPhoneNumber());
        failureCount = failureCount + 1;
      }
    }

    return builder
        .failureList(failureList)
        .failureCount(failureCount)
        .successList(successList)
        .successCount(successCount)
        .build();
  }

  private R<Void> send(
      SmsApplication smsApplication,
      SmsApplicationConfig smsApplicationConfig,
      SmsSupplier smsSupplierConfig,
      SmsHandler smsHandler,
      String phoneNumber,
      String content) {

    SmsSendRecord sendRecord =
        smsSendRecordManager.addSendingRecord(
            smsApplication, smsSupplierConfig, phoneNumber, content);

    R<Void> sendResult;
    try {
      sendResult = smsHandler.send(phoneNumber, content);
    } catch (Exception ex) {
      log.error("send_sms_fail", ex);
      sendResult = R.fail("send sms fail");
    }

    if (sendResult.success()) {
      this.smsSendRecordManager.updateSuccess(sendRecord.getId(), "发送成功");
    } else {
      this.smsSendRecordManager.updateFailure(
          sendRecord.getId(), "发送失败." + sendResult.getMessage());
    }
    return sendResult;
  }

  private R<Void> sendTemplate(
      SmsApplication smsApplication,
      SmsApplicationConfig smsApplicationConfig,
      SmsSupplier smsSupplierConfig,
      SmsHandler smsHandler,
      SendTemplateInput sendTemplateInput) {

    String realContent =
        this.transformSmsContent(
            smsApplicationConfig.getTemplateContent(), sendTemplateInput.getContentParams());

    Map<String, String> realContentMap = this.transformRealContentMap(realContent);

    SmsSendRecord sendRecord =
        smsSendRecordManager.addSendingRecord(
            smsApplication, smsSupplierConfig, sendTemplateInput.getPhoneNumber(), realContent);

    R<Void> sendResult;
    try {
      sendResult = smsHandler.send(sendTemplateInput.getPhoneNumber(), realContentMap);
    } catch (Exception ex) {
      log.error("send_sms_fail", ex);
      sendResult = R.fail("send sms fail");
    }

    if (sendResult.success()) {
      this.smsSendRecordManager.updateSuccess(sendRecord.getId(), "发送成功");
    } else {
      this.smsSendRecordManager.updateFailure(
          sendRecord.getId(), "发送失败." + sendResult.getMessage());
    }
    return sendResult;
  }

  private String transformSmsContent(String templateContent, Map<String, String> contentParams) {
    if (StringUtils.isBlank(templateContent)) {
      return StringUtils.EMPTY;
    }
    String realContent = StringSubstitutor.replace(templateContent, contentParams);
    return realContent;
  }

  private Map<String, String> transformRealContentMap(String realContent) {
    return this.jsonService.toMap(realContent, String.class, String.class);
  }
}
